<script type="text/javascript">
var LBUTTON = 0, MBUTTON = 1, RBUTTON = 2;
var LEFT = 'L', RIGHT = 'R', UP = 'U', DOWN = 'D';

var OPTION_URL = 'chrome-extension://' + chrome.extension.id_ + '/options.html';

var config, def_config = {
	trigger: RBUTTON,	// mouse button to trigger stroke
	timeout: 200,		// a stroke sequence expires after timeout (milliseconds)
	minstep: 20,		// minimum moving distance of a stroke (in px)
	strokes: {
		/* navigation */
		'to-page-top': [UP],
		'to-page-bottom': [DOWN],
		'history-back': [LEFT],
		'history-forward': [RIGHT],
		'previous-tab': [UP, LEFT],
		'next-tab': [UP, RIGHT],

		/* window */
		//'minimize-window': [DOWN, LEFT],
		'new-window': [UP, DOWN, RIGHT, UP],
		'close-window': [LEFT, DOWN, RIGHT],

		/* tab */
		'new-tab': [RIGHT, DOWN],
		'close-tab': [LEFT, UP, RIGHT],
		'detach-tab': [LEFT, UP],

		/* utilities */
		'reload': [UP, DOWN],
		'skip-cache-reload': [UP, DOWN, UP],
		'view-source': [LEFT, DOWN, RIGHT, DOWN, LEFT],

		/* open help page */
		'help': [DOWN, UP, RIGHT, DOWN]
	}
};

var actions = {
	'to-page-top': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.executeScript(tab.id, {
				code: 'document.body.scrollTop = 0'
			});
		});
	},
	'to-page-bottom': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.executeScript(tab.id, {
				code: 'document.body.scrollTop = document.body.scrollHeight'
			});
		});
	},
	'history-back': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.executeScript(tab.id, {
				code: 'history.back()'
			});
		});
	},
	'history-forward': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.executeScript(tab.id, {
				code: 'history.forward()'
			});
		});
	},
	'previous-tab': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.getAllInWindow(null, function(tabs) {
				for (var i = 0; i < tabs.length; ++i) {
					if (tabs[i].id == tab.id) {
						if (!!(tab = tabs[i - 1])) {
							chrome.tabs.update(tab.id, {selected: true});
						}
						break;
					}
				}
			});
		});
	},
	'next-tab': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.getAllInWindow(null, function(tabs) {
				for (var i = 0; i < tabs.length; ++i) {
					if (tabs[i].id == tab.id) {
						if (!!(tab = tabs[i + 1])) {
							chrome.tabs.update(tab.id, {selected: true});
						}
						break;
					}
				}
			});
		});
	},
	'minimize-window': function() {
//		chrome.windows.getCurrent(function(wnd) {
//			chrome.windows.update(wnd.id, { left: 0, top: 0, width: 0, height: 0 });
//		});
	},
	'new-window': function() {
		chrome.windows.create();
	},
	'close-window': function() {
		chrome.windows.getCurrent(function(wnd) {
			chrome.windows.remove(wnd.id);
		});
	},
	'new-tab': function() {
		chrome.tabs.create({});
	},
	'close-tab': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.remove(tab.id);
		});
	},
	'detach-tab': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.windows.create(null, function(wnd) {
				chrome.tabs.move(tab.id, { windowId: wnd.id, index: 0 });
				chrome.tabs.getAllInWindow(wnd.id, function(tabs) {
					for (var i = 0; i < tabs.length; ++i) {
						if (tabs[i].id != tab.id) {
							chrome.tabs.remove(tabs[i].id);
						}
					}
				});
			});
		});
	},
	'reload': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.executeScript(tab.id, {
				code: 'location = location'
			});
		});
	},
	'skip-cache-reload': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.executeScript(tab.id, {
				code: 'location.reload()'
			});
		});
	},
	'view-source': function() {
		chrome.tabs.getSelected(null, function(tab) {
			chrome.tabs.create({
				url: 'view-source:' + tab.url
			});
		});
	},
	'help': function() {
		chrome.tabs.create({
			url: OPTION_URL
		});
	}
};

/* compare stroke sequences */
var match = function(seq1, seq2) {
	if (seq1 === seq2 || !seq1 && !seq2) return true;
	if (!seq1 && seq2 || seq1 && !seq2 || seq1.length != seq2.length) return false;

	return seq1.join(' ') == seq2.join(' ');
}

/* load user config */
var load_config = function(update_tabs) {
	try {
		if (!(config = JSON.parse(localStorage.getItem('config')))) {
			config = def_config;
		}
	} catch (ex) {
		console.error(ex);
		config = def_config;
	}

	if (!update_tabs) return;

	/* update config in all tabs */
	var code = get_config_update_code();
	chrome.windows.getAll({populate: true}, function(windows) {
		var i, j, tabs;
		for (i = 0; i < windows.length; ++i) {
			tabs = windows[i].tabs;
			for (j = 0; j < tabs.length; ++j) {
				chrome.tabs.executeScript(tabs[j].id, {code: code});
			}
		}
	});
}

/* generate code to fire a customized set-config-event in the embedding page */
var get_config_update_code = function() {
	return '\
window["mouse-stroke-config"] = JSON.parse(\'' + JSON.stringify(config) + '\');	\
var e = document.createEvent("Event");						\
e.initEvent("set-config-event", true, true);					\
setTimeout(function() { document.dispatchEvent(e) }, 500);			\
';
}

/* inject code to detect strokes */
chrome.tabs.onUpdated.addListener(function(id, info) {
	if ('complete' !== info.status) return;
	chrome.tabs.executeScript(id, {file: 'stroke.js'});
	/* inject configs, communication between extension and embedding page
	 * has to be done through shared DOM objects, see:
	 * http://code.google.com/chrome/extensions/content_scripts.html#host-page-communication */
	chrome.tabs.executeScript(id, {code: get_config_update_code()});
});

/* listen to the stroke detecting script */
chrome.extension.onConnect.addListener(function(port) {
	if ('stroke-tunnel' != port.name) return;
	/* don't respond to option page */
	if (port.tab.url == OPTION_URL) return;

	port.onMessage.addListener(function(strokes) {
		var i, stroke;

		for (id in config.strokes) {
			if (match(strokes, config.strokes[id])) {
				actions[id].apply();
				break;
			}
		}
	});
});

/* listen to the config script */
chrome.extension.onConnect.addListener(function(port) {
	if ('config-tunnel' != port.name) return;

	port.onMessage.addListener(function(command) {
		if ('reload' == command) {
			load_config(true);
		}
	});
});

/* always refresh def_config */
localStorage.setItem('def_config', JSON.stringify(def_config));
/* get user config */
load_config();
</script>